#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "FileDialog.h"

#include "Class.h"
#include "PopupItem.h"
#include "BorderItems.h"
#include "Buttons.h"
#include "Box.h"
#include "DataListView.h"
#include "FileData.h"
#include "Fields.h"
#include "Menu.h"
#include "Alert_e.h"
#include "Scroller.h"
#include "Document.h"
#include "Application.h"
#include "Error.h"
#include "String.h"
#include "System.h"
#include "OrdColl.h"

//---- FileDialog --------------------------------------------------------------

NewMetaImpl(FileDialog, Dialog, (TP(eti), TP(title), TP(initDir), TE(flags),
		TP(gridview), TP(pathList), TP(doc), TP(fileData), TP(pathPopup),
		TP(current)));

FileDialog::FileDialog(const char *ti) : Dialog(ti)
{
	title= new TextItem(ti);
	eti= new TextField(cIdName, 20);
	doc= 0;
	fileData= 0;
	initDir= 0;
	pathList= 0;
	current= 0;
}

FileDialog::~FileDialog()
{
	SafeUnref(fileData);
	SafeDelete(initDir);
	SafeDelete(gridview);
	if (pathList) {
		pathList->FreeAll();
		SafeDelete(pathList);
	}
	SafeUnref(current);
}

const char *FileDialog::FileName()
{
	return fileData->FullName();
}

int FileDialog::ShowInWindow(FileDialogFlags f, Clipper *fp, EvtHandler *eh,
																const char *p)
{
	flags= f;
	doc= eh;

	if (p == 0) {
		if (f == eFDRead)
			p= "Open File Named:";
		else if (f == eFDWrite)
			p= "Save in File Named:";
		else
			p= "Import File Named:";
	}

	if (title)
		title->SetString(p);

	// remember initial working directory
	strreplace(&initDir, gSystem->WorkingDirectory());

	return Dialog::ShowOnWindow(fp);
}

void FileDialog::UpdateList()
{
	SafeUnref(current);
	current= new FileData(".", FALSE);
	current->Ref();

	SeqCollection *contents= current->GetContents();
	Iter next(contents);
	register Data *d, *d2;

	OrdCollection *files= new OrdCollection;
	while (d= (Data*) next())
		if (d2= FilterData(d))
			files->Add(d2);
	files->Sort();
	gridview->SetCollection(files);
	SafeDelete(contents);

	if (eti)
		eti->SetString("");
}

Data *FileDialog::FilterData(Data *data)
{
	return data;
}

VObject *FileDialog::DoMakeControls()
{
	return
		new VExpander(20,
			MakePart(eFDTopPart),
			MakePart(eFDListPart),
			new VExpander(gPoint4,
				MakePart(eFDEditTitlePart),
				MakePart(eFDEditPart),
				0
			),
			0
		);
}

VObject *FileDialog::DoMakeActions()
{
	return MakePart(eFDActionPart);
}

VObject *FileDialog::MakePart(int which)
{
	switch (which) {

	case eFDTopPart:
		pathPopup= new PopupButton(cIdPath, cIdNone,
				new Menu("Current Directory", FALSE, FALSE));
		return
			new Form(cIdNone, (VObjAlign)(eVObjVBase+eVObjHExpand), gPoint10,
				"Current Directory:", pathPopup,
				0
			);

	case eFDListPart:
		gridview= new DataListView(cIdList, this, cItemMinWidth);
		scroller= new Scroller(gridview,
				Point(cItemMinWidth, DataListView::RowHeight()*cNumItems), cIdList);
		return scroller;
		
	case eFDEditPart:
		return eti;
		
	case eFDEditTitlePart:
		return title;

	case eFDActionPart:
		return new HBox(20, (VObjAlign)(eVObjVBase|eVObjHEqual|eVObjHExpand),
			openButton= new ActionButton(cIdOk, flags == eFDWrite ? "Save" : "Open", TRUE),
			new ActionButton(cIdCancel, "Cancel"),
			new ActionButton(cIdUpdate, "Update"),
			new ActionButton(cHELP, "Help"),
			0
		);

	default:
		break;
	}
	return 0;
}

void FileDialog::DoSetDefaults()
{
	UpdatePath();
	UpdateList();
}

void FileDialog::DoSetup()
{
	if (eti) {
		if (flags == eFDWrite) {
			char buf[1000];
			eti->GetString(buf, 1000);
			FileData *fd= new FileData(buf, FALSE);
			openButton->SetLabel(fd->IsDirectory() ? "Open" : "Save", TRUE);
			delete fd;
		}
		openButton->Enable(eti->GetTextSize() > 0);
	}
}

void FileDialog::DoRestore()
{
	gSystem->ChangeDirectory(initDir);
}

void FileDialog::Control(int id, int p, void *v)
{
	Data *d;
	const char *fname;

	switch (id) {

	case cIdUpdate:
		UpdateList();
		return;

	case cIdList:
		switch (p) {
		case cPartCollSelect:
			d= (Data*) gridview->ObjAt(Point(0,(int)v));
			if (d) {
				fname= d->ShortName();
				if (eti && fname && strlen(fname) > 0)
					eti->SetString(fname);
			}
			break;
		case cPartCollDoubleSelect:
			if (OpenOrChangeDir()) {
				Dialog::Control(cIdOk, cPartAction, v);
				return;
			}
			break;
		}
		return;
		
	case cIdOk:
		if (!OpenOrChangeDir())
			return;
		break;
		
	case cIdPath:
		d= (Data*) pathList->At((int)v-cIdComponent);
		if (d && eti)
			eti->SetString(d->FullName());
		OpenOrChangeDir();
		return;
	}
	Dialog::Control(id, p, v);
}

void FileDialog::InputKbd(Token &t)
{
    if (t.IsAscii() && scroller->ContainsPoint(t.Pos)) {
	if (t.Code != '\r' && t.Code != '\n')
	    scroller->Input(t.Pos, t, GetWindow());
    } else
	Manager::InputKbd(t);
}

bool FileDialog::ChangeDirectory()
{
	if (fileData->IsDirectory()) {
		if (!gSystem->ChangeDirectory(fileData->FullName()))
			ShowAlert(eAlertNote, "Cannot change directory to @B%s@P", fileData->FullName());
		else {
			UpdatePath();
			UpdateList();
		}
		return TRUE;
	}
	return FALSE;
}

bool FileDialog::OpenOrChangeDir()
{
	char pathname[cPathBuf];
	eti->GetString(pathname, cPathBuf);

	if (gSystem->ExpandPathName(pathname, cPathBuf-1)) {
		ShowAlert(eAlertNote, "%s (%s)", gSystem->GetErrorStr(), pathname);
		return FALSE;
	}

	SafeUnref(fileData);
	fileData= new FileData(pathname, TRUE); // deep
	fileData->Ref();

	if (ChangeDirectory())
		return FALSE;
		
	const char *fname= fileData->ShortName();

	if (flags == eFDWrite) {
		if (!gSystem->AccessPathName(fname, 0)) {
			if (ShowAlert(eAlertCaution, "File @B%s@P exists! Overwrite ?", fname) != cIdYes)
				return FALSE;
		}
		if (!fileData->IsWritable()) {
			ShowAlert(eAlertNote, "Document @B%s@P is not writable\n%s", fname,
															gSystem->GetErrorStr());
			return FALSE;
		}
	} else {
		if (!fileData->IsReadable()) {
			ShowAlert(eAlertNote, "Can't open document @B%s@P for reading\n%s",
												fname, gSystem->GetErrorStr());
			return FALSE;
		}
		if (WrongType()) {
			ShowAlert(eAlertNote, "File @B%s@P has wrong type (%s)", fname,
											fileData->Type().AsString());
			return FALSE;
		}
	}
	return TRUE;
}

bool FileDialog::WrongType()
{
//	if (doc) {
//		if (doc->IsKindOf(Document)) {
//			if (flags == eFDImport)
//				filterfunc= (FilterFunc) &Document::CanLoad;
//			else
//				filterfunc= (FilterFunc) &Document::CanLoad;
//		} else if (doc->IsKindOf(View)) {
//			View *v= (View*) doc;
//			return ! v->CanPaste(fileData);
//		} else
//			filterfunc= (FilterFunc) &Application::CanOpen;
//		return ! (doc->*filterfunc)(fileData);
//	}
//	return FALSE;
	if (doc == 0)
		return FALSE;
	if (doc->IsKindOf(Document))
		return ! Guard(doc,Document)->CanLoad(fileData);
	if (doc->IsKindOf(View))
		return ! Guard(doc,View)->CanPaste(fileData);
	if (doc->IsKindOf(Application))
		return ! Guard(doc,Application)->CanOpen(fileData);
	return FALSE;

}

void FileDialog::UpdatePath()
{
	Data *here;
	if (pathList) {
		pathList->FreeAll();
		SafeDelete(pathList);
	}
	pathList= new OrdCollection;
	for (here= new FileData("."); here; here= here->GetParent())
		pathList->AddFirst(here);

	Iter next(pathList);
	Data *dp;
	SeqCollection *newpath= new OrdCollection;
	for (int i= 0; dp= (Data*)next(); i++)
		newpath->Add(new MenuButtonItem(cIdComponent+i, dp->ShortName()));

	pathPopup->SetCollection(newpath);
	pathPopup->SetValue(cIdComponent+i-1);
}

